import StackTrace from "stacktrace-js";
import { TauriError } from "./backend";
import { AppLog } from "./utils";

export class AppError extends Error {
    original?: Error;
    constructor(input: unknown, options?: { code?: number | string, name?: string }) {
        const data = AppError.parse(input, options);
        super(data.message);
        Object.assign(this, data);
    }
    static parse(input: unknown, options?: { code?: number | string, name?: string }) {
        if (input instanceof AppError) {
            return {
                message: input.message,
                name: input.name,
                original: input.original ?? input,
            };
        }
        if (input instanceof Error) {
            return {
                message: input.message,
                name: options?.name ?? input.name ?? 'AppError',
                original: input,
            };
        }
        if (typeof input === 'string') {
            return {
                message: options?.code ? `${input} (${options.code})` : input,
                name: options?.name ?? 'AppError',
            };
        }
        // fallback
        const err = input as TauriError;
        const message = err.message + (err.code ? ` (${err.code})` : '');
        return {
            message,
            name: options?.name ?? 'AppError',
        };
    }
    async handle() {
        let frames: StackTrace.StackFrame[] = [];
        try {
            frames = await StackTrace.fromError(this.original ?? this);
        } catch(_) {}
        const filtered = frames.filter(f =>
            f.fileName && !f.fileName.includes('/node_modules')
        );
        console.log('Got StackFrame for ' + this.message + '\n', filtered);
        const stack = filtered.map(v => {
            const file = v.fileName?.match(/^(?:https?|file):\/\/[^/]+(\/[^\s?#]*)/)?.[1] ?? '<anonymous>';
            const path = `${file}:${v.lineNumber}:${v.columnNumber}`;
            return `    at ${path}`;
        }).join('\n');
        return AppLog(`${this.name}: ${this.message}\n` + stack, 'error');
    }
}